﻿using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace XFGameFramework.OctreeSystem
{

    public class OctreeArea
    {

        #region 常量

        [Tooltip("容量")]
        private const int CAPACITY = 25; 
        #endregion

        #region 字段

        private List<OctreeObject> allObjects = null;

        private List<OctreeObject> objects = new List<OctreeObject>();

        private OctreeArea up_left_up = null;
        private OctreeArea up_left_down = null;
        private OctreeArea up_right_up = null;
        private OctreeArea up_right_down = null;
        private OctreeArea down_left_up = null;
        private OctreeArea down_left_down = null;
        private OctreeArea down_right_up = null;
        private OctreeArea down_right_down = null;

        private OctreeArea parent = null;

        [Tooltip("层级")]
        private int layer;

        #endregion


        #region 属性

        public Bounds Bounds { get; private set; }
          
        /// <summary>
        /// 是否有子节点
        /// </summary>
        public bool IsHaveChild => up_left_up != null;

        /// <summary>
        /// 当前节点及其子节点中的所有游戏物体
        /// </summary>
        private List<OctreeObject> AllObjects 
        {
            get 
            {
                if(allObjects == null)
                    allObjects = new List<OctreeObject>();

                allObjects.Clear();

                allObjects.AddRange(objects);

                if (IsHaveChild) 
                {
                    allObjects.AddRange(up_left_up.AllObjects);
                    allObjects.AddRange(up_left_down.AllObjects);
                    allObjects.AddRange(up_right_up.AllObjects);
                    allObjects.AddRange(up_right_down.AllObjects);
                    allObjects.AddRange(down_left_up.AllObjects);
                    allObjects.AddRange(down_left_down.AllObjects);
                    allObjects.AddRange(down_right_up.AllObjects);
                    allObjects.AddRange(down_right_down.AllObjects); 
                }
                

                return allObjects;
            }
        }

        /// <summary>
        /// 当前节点及其子节点中的所有游戏物体数量
        /// </summary>
        private int AllObjectCount 
        {
            get 
            {
                int count = 0;

                count += objects.Count;

                if (IsHaveChild) 
                {
                    count += up_left_up.AllObjectCount;
                    count += up_left_down.AllObjectCount;
                    count += up_right_up.AllObjectCount;
                    count += up_right_down.AllObjectCount;
                    count += down_left_up.AllObjectCount;
                    count += down_left_down.AllObjectCount;
                    count += down_right_up.AllObjectCount;
                    count += down_right_down.AllObjectCount; 
                }

                return count;
            }
        }

        
        public int Layer => layer;

        #endregion

        #region 方法

        public OctreeArea(OctreeArea parent,int layer)
        {
            Bounds = new Bounds();
            this.parent = parent; 
            this.layer = layer;
        }
          
        public OctreeArea(OctreeArea parent,Bounds bounds, int layer)
        {
            Bounds = bounds;
            this.parent = parent;
            this.layer = layer;
        }


        internal void Add(OctreeObject obj)
        {
            if (!OctreeManager.Intersects(Bounds,obj)) return;

            // 判断是否有子节点 如果有子节点应该放到子节点中
            if (IsHaveChild)
            {
                up_left_up.Add(obj);
                up_left_down.Add(obj);
                up_right_up.Add(obj);
                up_right_down.Add(obj);
                down_left_up.Add(obj);
                down_left_down.Add(obj);
                down_right_up.Add(obj);
                down_right_down.Add(obj);
            }
            else
            {
                if (!objects.Contains(obj))
                    objects.Add(obj);

                obj.Area = this;

                if (objects.Count > CAPACITY && Bounds.size.x > OctreeManager.GetAreaMiniSize(layer))
                {
                    Vector3 offset = Bounds.size / 4;
                    Vector3 size = Bounds.size / 2;

                    Bounds bounds_up_left_up = new Bounds(Bounds.center + new Vector3(-offset.x, offset.y, offset.z), size);
                    Bounds bounds_up_left_down = new Bounds(Bounds.center + new Vector3(-offset.x, offset.y, -offset.z), size);
                    Bounds bounds_up_right_up = new Bounds(Bounds.center + new Vector3(offset.x, offset.y, offset.z), size);
                    Bounds bounds_up_right_down = new Bounds(Bounds.center + new Vector3(offset.x, offset.y, -offset.z), size);

                    Bounds bounds_down_left_up = new Bounds(Bounds.center + new Vector3(-offset.x, -offset.y, offset.z), size);
                    Bounds bounds_down_left_down = new Bounds(Bounds.center + new Vector3(-offset.x, -offset.y, -offset.z), size);
                    Bounds bounds_down_right_up = new Bounds(Bounds.center + new Vector3(offset.x, -offset.y, offset.z), size);
                    Bounds bounds_down_right_down = new Bounds(Bounds.center + new Vector3(offset.x, -offset.y, -offset.z), size);


                    up_left_up = new OctreeArea(this,bounds_up_left_up,this.layer);
                    up_left_down = new OctreeArea(this, bounds_up_left_down, this.layer);
                    up_right_up = new OctreeArea(this, bounds_up_right_up, this.layer);
                    up_right_down = new OctreeArea(this, bounds_up_right_down, this.layer);
                    down_left_up = new OctreeArea(this, bounds_down_left_up, this.layer);
                    down_left_down = new OctreeArea(this, bounds_down_left_down, this.layer);
                    down_right_up = new OctreeArea(this, bounds_down_right_up, this.layer);
                    down_right_down = new OctreeArea(this, bounds_down_right_down, this.layer);


                    // 把当前节点的游戏物体 放到子节点中

                    foreach (var item in objects)
                    {
                        Add(item);
                    }
                    objects.Clear();
                }
            }

        }

        /// <summary>
        /// 移除游戏物体
        /// </summary>
        /// <param name="obj">目标物体</param>
        /// <param name="updateBounds">是否合并空间(默认:false)</param>
        //internal void Remove(OctreeObject obj,bool updateBounds = false)
        //{
        //    if (!OctreeManager.Intersects(Bounds,obj)) return;

        //    if (objects.Contains(obj)) 
        //    {
        //        objects.Remove(obj); 
        //        if (updateBounds && parent != null)
        //            parent.UpdateBounds(); 
        //    }

        //    if (IsHaveChild)
        //    {
        //        up_left_up.Remove(obj);
        //        up_left_down.Remove(obj);
        //        up_right_up.Remove(obj);
        //        up_right_down.Remove(obj);
        //        down_left_up.Remove(obj);
        //        down_left_down.Remove(obj);
        //        down_right_up.Remove(obj);
        //        down_right_down.Remove(obj);
        //    }   
        //}

        internal void RemoveDirect(OctreeObject obj, bool updateBounds = false) 
        { 
            if (!objects.Contains(obj))
                return; 

            objects.Remove(obj);

            if (updateBounds && parent != null)
                parent.UpdateBounds();
        }

        internal void Clear()
        {
            objects.Clear();
            if (IsHaveChild)
            {
                up_left_up.Clear();
                up_left_down.Clear();
                up_right_up.Clear();
                up_right_down.Clear();
                down_left_up.Clear();
                down_left_down.Clear();
                down_right_up.Clear();
                down_right_down.Clear();

                up_left_up = null;
                up_left_down = null;
                up_right_up = null;
                up_right_down = null;
                down_left_up = null;
                down_left_down = null;
                down_right_up = null;
                down_right_down = null;
            }
        }

        private void UpdateBounds()
        {
            if (!IsHaveChild)
                return;

            if (AllObjectCount > CAPACITY) 
                return;

            allObjects = AllObjects;
            Clear();
            objects.AddRange(allObjects); 
        }

        internal void Get(Bounds bounds,List<OctreeObject> results)
        {
            if (results == null)
                throw new System.Exception("results is null!");
            if (!Bounds.Intersects(bounds)) return;

            // 检查是否有非活跃的游戏物体 
            for (int i = objects.Count - 1; i >= 0 ; i--)
            {
                if(!objects[i] || !objects[i].gameObject.activeInHierarchy)
                    objects.RemoveAt(i);
            }
            
            if (bounds.Contains(Bounds))
            {
                results.AddRange(objects);
            }
            else 
            {
                foreach (var item in objects)
                {
                    // 判断游戏物体是否和该区域相交
                    if(item.Intersects(bounds)) 
                        results.Add(item);
                }
            }
             

            if (IsHaveChild) 
            {
                up_left_up.Get(bounds, results);
                up_left_down.Get(bounds, results);
                up_right_up.Get(bounds, results);
                up_right_down.Get(bounds, results);
                down_left_up.Get(bounds, results);
                down_left_down.Get(bounds, results);
                down_right_up.Get(bounds, results);
                down_right_down.Get(bounds, results);
            }
        }

        internal void Copy(OctreeArea other) 
        {
            foreach (var item in other.objects)
            {
                Add(item);
            }

            if (other.IsHaveChild) 
            {
                Copy(other.up_left_up);
                Copy(other.up_left_down);
                Copy(other.up_right_up);
                Copy(other.up_right_down);
                Copy(other.down_left_up);
                Copy(other.down_left_down);
                Copy(other.down_right_up);
                Copy(other.down_right_down); 
            }
        }

        #endregion

    }
}